-
Notifications
You must be signed in to change notification settings - Fork 57
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[multipart] Support bytes[]/content type
#2380
Conversation
packages/autorest.python/autorest/codegen/templates/vendor.py.jinja2
Outdated
Show resolved
Hide resolved
packages/typespec-python/test/azure/mock_api_tests/test_payload_multipart_file_array.py
Outdated
Show resolved
Hide resolved
body.update(data) | ||
with pytest.raises(TypeError): | ||
# caused by deepcopy when DPG model init | ||
op(model_class(body)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
DPG model will raise error when deepcody of init if property value is io. @iscai-msft
@@ -23,7 +23,9 @@ | |||
from .constant_type import ConstantType | |||
from .utils import add_to_description | |||
from .combined_type import CombinedType | |||
from .model_type import JSONModelType | |||
from .model_type import JSONModelType, DPGModelType |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
wit this dpg PR Azure/typespec-azure#166 all we have to do is create a new type MultipartFileType
and then type this type as Union[binary, Tuple[string, binary], Tuple[string, binary, string]]
packages/autorest.python/autorest/codegen/serializers/builder_serializer.py
Outdated
Show resolved
Hide resolved
|
||
# file-like tuple could be `(filename, IO (or bytes))` or `(filename, IO (or bytes), content_type)` | ||
FileType = Union[IOBase, bytes] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would use the type hints I have here from core https://github.com/Azure/azure-sdk-for-python/blob/main/sdk/core/azure-core/azure/core/rest/_helpers.py#L79
return file | ||
return NamedBytesIO("auto-name-" + str(uuid.uuid4()), file) | ||
return ("auto-name-" + str(uuid.uuid4()), file) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why are we giving auto names to these file fields? That's def not what we should be doing. We can get the field name from the typespec and then create a tuple of all of the files
entries.
So
class MyMultipartModel:
profile_image: MultipartFile = rest_field("profileImage")
pictures: MultipartFile[] = rest_field("pictures")
def my_multipart_func(body: MyMultipartModel):
_files = [("profileImage", body.profile_image)]
_files.extend([("pictures", p)] for p in body.pictures)
request = HttpRequest(..., files=_files)
...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why are we giving auto names to these file fields?
-> According to https://www.rfc-editor.org/rfc/rfc7578#section-4.2, file name SHOULD be supplied. In my local test, the flask of python/multer of js
will not parse request into files
part if no file name provided. So I think SDK need to provide a name for it if users don't provide.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why are we giving auto names to these file fields? That's def not what we should be doing. We can get the field name from the typespec and then create a tuple of all of the
files
entries.So
class MyMultipartModel: profile_image: MultipartFile = rest_field("profileImage") pictures: MultipartFile[] = rest_field("pictures") def my_multipart_func(body: MyMultipartModel): _files = [("profileImage", body.profile_image)] _files.extend([("pictures", p)] for p in body.pictures) request = HttpRequest(..., files=_files) ...
Current logic is actually similar to yours and it also compatible when body type is JSON
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
SHOULD doesn't mean MUST. If the IO is not a file descriptor and we don't have a name, we don't set it. No autoname, this will be sent to the servirce, that may save it, or even act based on the received name.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Reading the code more, this function should not exist
@@ -278,6 +280,25 @@ def has_json_model_type(self) -> bool: | |||
return self.type.target_model_subtype((JSONModelType,)) is not None | |||
return isinstance(self.type, JSONModelType) | |||
|
|||
@property |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
also we can actually support data
and files
entered, and pass them as HttpRequest(data=data, files=files)
once this pr gets merged and released Azure/azure-sdk-for-python#34021
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Before #2380 (comment), it passes data
and files
. Now the code merge all part into files and it works. SDK users will not be influenced no matter SDK merge all in files
or pass data
/files
so I think current logic is OK.
packages/typespec-python/test/azure/mock_api_tests/test_payload_multipart_file_array.py
Outdated
Show resolved
Hide resolved
…multipart-file-array-dev5
…multipart-file-array-dev5
…multipart-file-array-dev5
...ec-python/test/azure/generated/payload-multipart/payload/multipart/operations/_operations.py
Outdated
Show resolved
Hide resolved
@@ -52,7 +52,7 @@ def client(): | |||
def test_multi_part(client: MultiPartClient, op_name, model_class, data, file): | |||
op = getattr(client.form_data, op_name) | |||
# test bytes | |||
body = {k: open(str(v), "rb").read() for k, v in file.items()} | |||
body = {k: ("blob", open(str(v), "rb").read(), "application/octet-stream") for k, v in file.items()} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Lack test for binary_array_parts
/complex
, and you can refer to my previous commit:
autorest.python/packages/typespec-python/test/azure/mock_api_tests/test_payload_multipart.py
Lines 56 to 71 in fba4a91
"binary_array_parts", | |
models.BinaryArrayPartsRequest, | |
{"id": "123"}, | |
{"pictures": [PNG, PNG]}, | |
{}, | |
), | |
( | |
"complex", | |
models.ComplexPartsRequest, | |
{"id": "123", "previousAddresses": [models.Address(city="Y"), models.Address(city="Z")], "address": models.Address(city="X")}, | |
{"pictures": [PNG, PNG], "profileImage": JPG}, | |
{}, | |
), | |
], | |
) | |
def test_multi_part(client: MultiPartClient, op_name, model_class, data, file, file_info): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
oh ok, i was confused by the presence of this file since it's generic, it should be in generic_mock_api_+tests
fixes #2378, #2366
pending on Azure/azure-sdk-for-python#33948, Azure/azure-sdk-for-python#34021